// Copyright (c) Huawei Technologies Co., Ltd. 2022-2022. All rights reserved.

// Package config 全局配置类
package config

import (
	"codehub-g.huawei.com/videocloud/mediaprocesscenter/application-gateway/pkg/utils/security"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"strconv"
	"strings"

	"github.com/pborman/uuid"
	"github.com/pkg/errors"
)

const (
	LogLevelDebug            = "debug"
	LogLevelInfo             = "info"
	DeployModelSingleton     = "singleton"
	DeployModelMultiInstance = "multi-instances"
	DefaultLogRotateSize     = 1000
	DefaultLogBackupCount    = 100
	DefaultLogMaxAge         = 14
	AddressLength            = 2
	DefaultAuxproxyIPType    = "publicIP"

	DefaultCleanUpDays = 14
	DefaultBackupDays  = 3
)

const (
	GatewayAddress = "GATEWAY_ADDR"
	AassAddress    = "AASS_ADDR"

	// mysql
	DatabaseName     = "DATABASE_NAME"
	DatabaseAddress  = "DATABASE_ADDR"
	DatabaseUser     = "DATABASE_USER"
	DatabasePassword = "DATABASE_PASSWORD"

	// influxDB
	InfluxUser     = "INFLUX_USER"
	InfluxPassword = "INFLUX_PASSWORD"
	InfluxAddress  = "INFLUX_ADDR"
	InfluxDBName   = "INFLUX_DBNAME"

	// redis
	RedisAddress  = "REDIS_ADDR"
	RedisPassword = "REDIS_PASSWORD"
	RedisDatabase = "REDIS_DATABASE"

	// cipher
	GCMKey   = "GCM_KEY"
	GCMNonce = "GCM_NONCE"

	// log setting
	LogLevel       = "LOG_LEVEL"
	LogRotateSize  = "LOG_ROTATE_SIZE"
	LogBackupCount = "LOG_BACKUP_COUNT"
	LogMaxAge      = "LOG_MAX_AGE"

	// auxproxy ip type
	AuxproxyIPType = "AUXPROXY_IP_TYPE"

	// clean
	CleanUp       = "CLEAN_UP" // days
	Backup        = "BACKUP"   // days
	CleanStrategy = "CLEAN_STRATEGY"

	// other
	DeployMode = "DEPLOY_MODE"
)

const (
	DefaultGatewayAddress = "0.0.0.0:60003"
	DefaultRedisDatabase  = 11
)

type Config struct {
	GatewayAddr string
	AassAddr    string

	DbUserName string
	DbPassword string
	DbAddr     string
	DbName     string

	RedisAddress  string
	RedisPassword string
	RedisDB       int

	InfluxUsername string
	InfluxPassword string
	InfluxAddr     string
	InfluxDBName   string

	GCMKey   string
	GCMNonce string

	CleanStrategy string

	InstanceName string

	// 支持配置debug和info，输入是非debug，都是info
	LogLevel       string
	LogRotateSize  int
	LogBackupCount int
	LogMaxAge      int
	// 支持配置singleton，如果是singleton就是单实例运行，不会使用分布式锁的主备功能，其他字段都是启用分布式锁
	// 主要是为了可以快速恢复服务
	DeployModel string

	// 连接auxproxy选择公网或私网IP
	AuxproxyIPType string

	// 备份清理数据的参数
	CleanupDays int
	BackupDays  int
}

type ClientHmacConfig struct {
	// map 的 key 为目标服务
	Keys map[string]HmacLocalEntry `json:"keys"`
}

// HmacLocalEntry 本地访问远端hmac秘钥
type HmacLocalEntry struct {
	// 是否对请求进行 hmac签名
	Enable   bool   `json:"enable"`
	AK       string `json:"ak"`
	SKCypher []byte `json:"sk"`
}

type ServerHmacConfig struct {
	AuthEnable    bool             `json:"auth_enable"`
	ExpireSeconds int              `json:"expire_seconds"`
	Keys          []HmacStoreEntry `json:"keys"`
}

// HmacStoreEntry 远端访问本地hmac秘钥
type HmacStoreEntry struct {
	AK       string `json:"ak"`
	SKCypher []byte `json:"sk"`
}

var (

	// GlobalConfig 全局配置相关
	GlobalConfig Config

	// HttpsAddr 配置项
	HttpsAddr string
	HttpsPort int

	// ClientHmacConf ServerHmacConf hmac相关
	ClientHmacConf ClientHmacConfig
	ServerHmacConf ServerHmacConfig
)

func Init() error {

	GlobalConfig.GatewayAddr = getEnvString(GatewayAddress, "")
	GlobalConfig.AassAddr = getEnvString(AassAddress, "")
	GlobalConfig.GCMKey = getEnvString(GCMKey, "")
	GlobalConfig.GCMNonce = getEnvString(GCMNonce, "")

	err := loadDBConfig()
	if err != nil {
		return err
	}
	err = loadInfluxDbConfig()
	if err != nil {
		return err
	}
	err = loadRedisConfig()
	if err != nil {
		return err
	}
	err = loadLogConfig()
	if err != nil {
		return err
	}
	err = loadCleanConfig()
	if err != nil {
		return err
	}
	GlobalConfig.AuxproxyIPType = getEnvString(AuxproxyIPType, DefaultAuxproxyIPType)
	GlobalConfig.DeployModel = getEnvString(DeployMode, DeployModelMultiInstance)

	hostname, err := os.Hostname()
	if err != nil {
		hostname = ""
	}

	GlobalConfig.InstanceName = fmt.Sprintf("%s-%s", hostname, uuid.NewRandom().String())

	err = initHttpsSettings()
	if err != nil {
		return err
	}

	err = initHmacSettings()
	if err != nil {
		return err
	}

	return nil
}

func initHttpsSettings() error {
	s := strings.Split(GlobalConfig.GatewayAddr, ":")
	if len(s) != AddressLength {
		return fmt.Errorf("invalid http address")
	}
	HttpsAddr = s[0]
	var err error
	HttpsPort, err = strconv.Atoi(s[1])
	return err
}

func initHmacSettings() error {

	var (
		bytes []byte
		err   error
	)

	// 本地访问远端配置
	if bytes, err = ioutil.ReadFile(ClientHmacConfFilePath); err != nil {
		return errors.New("read client hmac conf file err")
	}
	err = ToObject(bytes, &ClientHmacConf)
	if err != nil {
		return errors.New("unmarshal env ClientHmacConf err")
	}

	// 远端访问本地配置
	if bytes, err = ioutil.ReadFile(ServerHmacConfFilePath); err != nil {
		return errors.New("read server hmac conf file err")
	}
	err = ToObject(bytes, &ServerHmacConf)
	if err != nil {
		return errors.New("unmarshal env ServerHmacConf err")
	}
	return nil
}

// ToObject change data to object
func ToObject(data []byte, v interface{}) error {
	err := json.Unmarshal(data, v)
	return err
}

// 载入mysql配置信息
func loadDBConfig() error {
	GlobalConfig.DbAddr = getEnvString(DatabaseAddress, "")
	GlobalConfig.DbName = getEnvString(DatabaseName, "")
	GlobalConfig.DbUserName = getEnvString(DatabaseUser, "")
	GlobalConfig.DbPassword = getEnvString(DatabasePassword, "")
	return nil
}

// 载入influxdb配置信息
func loadInfluxDbConfig() error {
	GlobalConfig.InfluxUsername = getEnvString(InfluxUser, "")
	GlobalConfig.InfluxPassword = getEnvString(InfluxPassword, "")
	GlobalConfig.InfluxAddr = getEnvString(InfluxAddress, "")
	GlobalConfig.InfluxDBName = getEnvString(InfluxDBName, "")
	return nil
}

// 载入redis配置信息
func loadRedisConfig() error {
	var err error
	GlobalConfig.RedisAddress = getEnvString(RedisAddress, "")
	GlobalConfig.RedisDB = getEnvInt(RedisDatabase, DefaultRedisDatabase)
	redisPasswordStrDec := getEnvString(RedisPassword, "")

	GlobalConfig.RedisPassword, err = security.GCM_Decrypt(redisPasswordStrDec,
		GlobalConfig.GCMKey, GlobalConfig.GCMNonce)
	if err != nil {
		return err
	}
	return nil
}

// 载入log的配置信息
func loadLogConfig() error {
	GlobalConfig.LogLevel = getEnvString(LogLevel, LogLevelInfo)
	GlobalConfig.LogRotateSize = getEnvInt(LogRotateSize, DefaultLogRotateSize)
	GlobalConfig.LogBackupCount = getEnvInt(LogBackupCount, DefaultBackupDays)
	GlobalConfig.LogMaxAge = getEnvInt(LogMaxAge, DefaultLogMaxAge)
	return nil
}

// 载入数据清理相关的配置信息
func loadCleanConfig() error {
	GlobalConfig.CleanStrategy = getEnvString(CleanStrategy, "off")
	GlobalConfig.CleanupDays = getEnvInt(CleanUp, DefaultCleanUpDays)
	GlobalConfig.BackupDays = getEnvInt(Backup, DefaultBackupDays)
	return nil
}

func getEnvString(key string, defaultValue string) string {
	value := os.Getenv(key)
	if value == "" {
		value = defaultValue
	}
	return value
}

func getEnvInt(key string, defaultValue int) int {
	value, err := strconv.Atoi(os.Getenv(key))
	if err != nil {
		return defaultValue
	}
	return value
}

func getEnvBool(key string, defaultValue bool) bool {
	value, err := strconv.ParseBool(os.Getenv(key))
	if err != nil {
		return defaultValue
	}
	return value
}
